/*
* This file is the main code of Texdi
* Copyright (C) <2016>  <alkeon> [alkeon@autistici.org]
   
* Texdi is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Texdi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Texdi.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <list>
#include "tedi2tex.h"
#include "exception.h"
using namespace std;
/*
 * This function converts a string with Tedi format and return 
 * a string with Latex format.
 * Header and footer are void by default.
 */
string tedi2tex(string text, string header, string footer){
	list<int> list_table=info_tex(text);
	stringstream index(text);
	string line;
	string return_text = header;

	int control=0;
	int open_brackets=0;
	while(getline(index,line)){
		size_t ul=line.find('_');
		size_t key1=line.find('{');
		size_t bra1=line.find("[(");
		if(line[0]!='<'){
			//Change all _ to \_ Latex uses _ as underscore
			while(ul!=std::string::npos){
				size_t ul2=line.find("__");
				if(ul2==std::string::npos){
					line=line.insert(ul,1,'\\');
				}
				ul=line.find('_',ul+2);
			}
			
			size_t hash=line.find('#');
			if(hash!=std::string::npos){
				line=title_tex(line);
				return_text+=line+"\n";
			}else{
				size_t ul=line.find("__");
				if(ul!=std::string::npos){
					return_text+="\\begin{itemize}\n";
				}
				size_t li=line.find("--");
				if(li!=std::string::npos){
					line=line.erase(li,2);

					line=line.insert(li,"\\item ");
				}
				size_t ulf=line.find(",,");
				if(ulf!=std::string::npos){
					return_text+="\\end{itemize}\n";
				}
				size_t comi=line.find("\"");
				if(comi!=std::string::npos){
					if(line[line.size()-1]=='"')
						line=line.substr(1,line.size()-2);
					else if(line[line.size()-2]=='"')
						line=line.substr(1,line.size()-3);
					else
						throw Invalid("Missing end quotes.",line);
				}else{
					int etiq=main_tag_tex(line);
					size_t key=line.find("{(");
					if(key==string::npos && key1!=string::npos)
						throw Invalid("Missing block tag.",line);
					size_t bra=line.find("[(");
					if(bra==string::npos && bra1!=string::npos)
						throw Invalid("Missing link tag.",line);
					while(etiq!=-1){
						if(etiq==2){
							size_t lla2=line.find("}");
							if(lla2!=std::string::npos){
								line=blocks_tex(line);
							}else{
								++open_brackets;
								line=incompletes_blocks_tex(line);
							}
						}else{
							if(etiq==0){
								size_t par1=line.find("([");
								if(par1!=std::string::npos )
									line=images_tex(line);
							}else{
								line=links_tex(line);
							}
						}
						etiq=main_tag_tex(line);
					}
					size_t lla2=line.find("}");
					size_t lla1=line.find("{");
					if(lla2!=std::string::npos && lla1==string::npos){
						if(open_brackets==0)
					   		throw Invalid("Missing '{' in block tag.",line);
						--open_brackets;
						line=line.erase(lla2,1);
					}
					
					size_t hor=line.find("|");
					if(control==0){
						size_t hor1=line.rfind("|");
						if(hor!=string::npos){
							if(hor1!=hor){
								return_text+="\\begin{tabular}{";
								int i=0;
								//std::cout << tabla.front() << std::endl;
								double size_per_column=0.9/list_table.front();
								while(i<list_table.front()){
									return_text+="|p{"+to_string(size_per_column)+"\\linewidth}";
									++i;
								}

								return_text+="|}\n";
								list_table.pop_front();
								table_tex(line,return_text);
								control=1;
							}else{
								throw Invalid("Missing '|' in table.",line);
							}
						}
					}else{
						if(hor==string::npos){
							return_text+="\\hline\n";
							return_text+="\\end{tabular}\n";
							control=0;
						}else{
							table_tex(line,return_text);
						}
					}

					size_t minor=line.rfind(" ");
					if(minor==line.size()-1 && line.size()>1){
						return_text+=line+ "\n\n";
					}else{
						if(ulf==string::npos && ul==string::npos && hash==string::npos){
							return_text+=line+ "\n";
						}
					}
				}
			}
		}else{
			if(line[1]!='!'){
				if(line[1]!='>'){
					line=line.erase(0,1);
					return_text+= "\\begin{verbatim}\n"+line+ "\n\\end{verbatim}\n\n";
				}else{
					line=line.erase(0,2);
					return_text+=line+ "\n\n";
				}
			}
		}
	}
	if(open_brackets>0)
		throw Invalid("Missing '}' in document.","End of file");
	return_text += footer;
	return return_text;
}

/*
 * Detects next tag
 * Returns 
 * 0: images
 * 1: links
 * 2: blocks
 * -1: No tag
 */
int main_tag_tex(string line){
	int etiq=-1;
	size_t cont=line.size();
	size_t lla=line.find("{(");
	size_t paren=line.find("([");
	size_t corch=line.find("[(");
	if(lla!= string::npos && lla<cont){
		etiq=2;
		cont=lla;
	}
	if(corch != string::npos && corch<cont){
		cont=corch;
		etiq=1;
	}
	if(paren != string::npos && paren<cont){
		etiq=0;
	}
	return etiq;
}
/*
 * Convert Tedi table to a HTML link (line by line)
 *
 */
void table_tex(string& line, string& return_text){
	size_t hor=line.find("|");
	if(hor!=string::npos){
		++hor;
		return_text+="\\hline\n";
		line=line.substr(hor,line.size()-1);
		hor=line.find("|");
		int tam=line.size()-1;
		int x=0;
		while(hor!=std::string::npos && line[tam]=='|'){
			std::string elemento;
			elemento=line.substr(0,hor);
			if(x!=0){
				return_text+=" &" +elemento;
			}else{
				return_text+=elemento;
			}
			++hor;
			line=line.substr(hor,line.size()-1);
			hor=line.find("|");
			tam=line.size()-1;
			++x;
		}
		return_text+="\\tabularnewline\n";
	}else
		throw Invalid("Missing table",line);
	
}
/*
 * Convert Tedi link to a Latex link
 *
 */
string links_tex(std::string line){
	size_t start_tag=line.find("[(");
	string aux="\\href{";
	if(start_tag!= string::npos){
		line=line.erase(start_tag,2);
		line=line.insert(start_tag,aux);
		size_t par1=line.find(")");
		if(par1!=string::npos){
			line=line.erase(par1,1);
			aux="}{";
			line=line.insert(par1,aux);
			size_t brack=line.rfind("]");
			if(brack!=string::npos){
				line=line.erase(brack,1);
				aux="}";
				line=line.insert(brack,aux);
			}else
				throw Invalid("Missing ']' in link tag.",line);
		}else
			throw Invalid("Missing ')' in link tag.",line);
	}else
		throw Invalid("Missing link tag.",line);
	return line;
}
/*
 * Deletes parentheses and adds Latex text of a image.
 *
 */
string images_tex(std::string line){

	size_t start_tag=line.find("([");
    string aux="\\begin{figure}[h]\n\\centering\n\\includegraphics[width=0.5\\textwidth]{";
	if(start_tag!= string::npos){
		line=line.erase(start_tag,2);
		size_t par1=line.rfind("]");
		if(par1!=string::npos){
			string caption=line.substr(start_tag,par1-start_tag);
			line=line.erase(start_tag, par1+1-start_tag);
			line=line.insert(start_tag,aux);

			size_t brack=line.rfind(")");
			if(brack!=string::npos){
				line=line.erase(brack,1);
				aux="}\n\\caption{"+ caption+"}\n\\end{figure}";
				line=line.insert(brack,aux);
			}else
				throw Invalid("Missing ')' in image tag.",line);
		}else
			throw Invalid("Missing ']' in image tag.",line);
	}else
		throw Invalid("Missing image tag.",line);
	return line;

}
/*
 * Deletes hashes and adds Latex text of a title.
 *
 */
string title_tex(std::string line){
	size_t exa1=line.find('#');
	int ti=-1;
	while(exa1!=std::string::npos && line[exa1]=='#'){
		++exa1;
		line=line.substr(exa1,line.size()-1);
		exa1=line.find('#');

		++ti;
	}
	if(ti==0)
		line="\\section{"+line+"}\n";
	else{
		if(ti==1)
			line="\\subsection{"+line+"}\n";
		else
			line="\\subsubsection{"+line+"}\n";
	}
	return line;
}
/*
 * Deletes brackes and parentheses.
 *
 */
string blocks_tex(std::string line){
	size_t key1=line.find("{(");

	if(key1!= std::string::npos){
		size_t par2=line.find(")");
		if(par2!=string::npos){
			int delete_class =par2-key1;
			line=line.erase(key1,delete_class+1);
			size_t key2=line.rfind("}");
			if(key2!=string::npos){
				line=line.erase(key2,1);
			}else
				throw Invalid("Missing '}' in block tag.",line);
		}else
			throw Invalid("Missing ')' in block tag.",line);
	}else
		throw Invalid("Missing block tag.",line);
	return line;
}
/*
 * Deletes brackes and parentheses.
 *
 */
string incompletes_blocks_tex(std::string line){
	size_t key1=line.find("{(");
	if(key1!= std::string::npos){
		size_t par2=line.find(")");
		if(par2!=string::npos){
			int delete_class =par2-key1;
			line=line.erase(key1,delete_class+1);
			size_t key2=line.find("}");
			if(key2!=string::npos)
				throw Invalid("Unexpected '}' in incomplete block tag.",line);
		}else
			throw Invalid("Missing ')' in block tag.",line);
	}else
		throw Invalid("Missing block tag.",line);
	return line;
}
/*
 * Detects size of a table and save it into a list
 *
 */ 
list<int> info_tex(std::string file){
	std::list<int> list_table;
	std::stringstream reader(file);
	std::string line;
	int check=0;
	while(getline(reader,line)){
		size_t np=line.find('<');
		if(np==std::string::npos){
			size_t table=line.find('|');
			if(table!=std::string::npos){
				size_t hor=line.find("|");
				if(check==0){
					int data=0;
					int tam=line.size()-1;
					while(hor!=std::string::npos && (line[tam]=='|' or line[tam-1]=='|')){
						std::string element;
						element=line.substr(0,hor);
						++hor;
						line=line.substr(hor,line.size()-1);
						hor=line.find("|");
						tam=line.size()-1;
						++data;
					}
					--data;
					list_table.push_back(data);
					check=1;
				}
			}else{
				check=0;
			}
		}
	}

	return list_table;
}
